#!/bin/sh
#
# Diskless Cluster Framework
# Make an initramfs for diskless nodes.
#
# $Id$
#
# Copyright (c) 2007 Erich Focht <efocht@hpce.nec.com>
#

ARCH=`uname -i`
MODULES="af_packet nfs"
KVERSION=
VERBOSE=
ROOTDIR=
OUTFILE=
SISOURCE=
MODSRC=
CLEANUP=

vecho() {
    [ -n "$VERBOSE" ] && echo $*
}

depends() {
    local mod=$1
    local line=`egrep "/$mod:" $MODSRC/modules.dep| sed -e "s,^.*/$mod:,,"`
    local deps m
    for m in $line; do
	m=`basename $m`
	[ -n "$m" ] || continue
	deps="$deps $m"
    done
    echo "$deps"
}

cleanup() {
    local f
    for f in $CLEANUP; do
	if [ -d "$f" ]; then
	    rm -rf $f
	elif [ -f "$f" ]; then
	    rm -f $f
	fi
    done
    exit 0
}

add_module() {
    local mpath=$1
    vecho "Adding module $mpath to my_modules"
    cp $mpath $TDIR/my_modules
    echo "insmod "`basename $mpath` >> $TDIR/my_modules/INSMOD_COMMANDS
}

is_absolute_path() {
    local p=$1
    if `echo $p | egrep "^/" >/dev/null`; then
	return 0
    else
	return 1
    fi
}

one_module() {
    #
    # this routine is called recursively
    #
    local m=$1
    local mpath mdeps

    vecho "one_module: $m"
    # if it's a full path, simply add it
    if is_absolute_path $m ; then
	vecho "Absolute path module: $m. Not checking dependencies."
	if [ -f $m ]; then
	    add_module $m
	else
	    echo "Module $m not found!"
	    exit 1
	fi
    fi

    # modules are names, strip of paths...
    m=`basename $m .ko`.ko
    # is the module available at all?
    mpath=`find $MODSRC -name "$m"`
    if [ ! -n "$mpath" ]; then
	vecho "Module $m not found in $MODSRC."
	vecho "Assuming it is compiled into the kernel."
	return
    fi
    
    # does this module have dependencies?
    mdeps=`depends $m`
    vecho "dependencies of $m: $mdeps."
    if [ -n "$mdeps" ]; then
	local d
	for d in $mdeps; do
	    vecho "Resolving dependency $d"
	    one_module $d
	done
    fi
    add_module $mpath
    return
}

load_modules() {
    local m

    for m in $MODULES; do
	vecho "load_modules: $m"
	one_module $m
    done
}


write_output() {
    cd $TDIR
    find . | cpio -o -H newc | gzip -9 > $OUTFILE
}


write_init() {

    cat > $1 <<-'EOF'
	INIT_PLACEHOLDER
	EOF

}

usage() {
    cat <<'EOF'

Usage:
	$0 [--arch|-a ARCH] [--module|-m module|none] [--kernel|-k VERSION] \
	   [--rootdir|-r PATH] [--template|-t PATH] [--verbose|-v] \
	   --output|-o OUTPUTFILE

OPTIONS:
  --arch|-a : specify architecture for selection of initrd template
  --module|-m : load module and its dependencies (order of -m options matters!)
  --kernel|-k : specify kernel version for modules to be loaded
  --rootdir|-r : look for modules in an image/chroot directory
  --template|-t : specify alternate place for the initrd template (default is
                  to look for the standard systemimager templates)
  --verbose|-v : verbose output
  --output|-o : specify target initrd file path.

EOF
}


trap cleanup SIGINT
trap cleanup SIGHUP
trap cleanup SIGKILL

while [ -n "$1" ]; do
    case $1 in
	--arch|-a)
	    shift
	    if [ -n "$1" ]; then
		ARCH=$1
		shift
	    else
		echo "Option --arch requires an argument!"
		exit 1
	    fi
	    ;;
	--module|-m)
	    shift
	    if [ -n "$1" ]; then
		if [ "$1" = "none" ]; then
		    MODULES=
		else
		    MODULES="$MODULES $1"
		fi
		shift
	    else
		echo "Option --module requires an argument!"
		exit 1
	    fi
	    ;;
	--kernel|-k)
	    shift
	    if [ -n "$1" ]; then
		KVERSION="$1"
		shift
	    else
		echo "Option --kernel requires an argument!"
		exit 1
	    fi
	    ;;
	--rootdir|-r)
	    shift
	    if [ -n "$1" ]; then
		ROOTDIR="$1"
		shift
	    else
		echo "Option --rootdir requires an argument!"
		exit 1
	    fi
	    ;;
	--output|-o)
	    shift
	    if [ -n "$1" ]; then
		OUTFILE="$1"
		shift
	    else
		echo "Option --output requires an argument!"
		exit 1
	    fi
	    ;;
	--template|-t)
	    shift
	    if [ -n "$1" ]; then
		SISOURCE="$1"
		shift
	    else
		echo "Option --template requires an argument!"
		exit 1
	    fi
	    ;;
	--verbose|-v)
	    shift
	    VERBOSE=1
	    ;;
	--help|-h)
	    shift
	    usage
	    exit 0
	    ;;
	*)
	    echo "Unknown option: $1"
	    exit 1
	    ;;
    esac
done

if [ ! -n "$KVERSION" ]; then
    echo "You MUST specify a kernel version!"
    usage
    exit 1
fi

MODSRC=$ROOTDIR/lib/modules/$KVERSION
if [ ! -e $MODSRC/modules.dep ]; then
    echo "Could not locate modules.dep in $MODSRC!"
    exit 1
fi

if [ ! -n "$OUTFILE" ]; then
    echo "No output file specified!"
    usage
    exit 1
else
    if ! is_absolute_path $OUTFILE ; then
	OUTFILE=`pwd`/$OUTFILE
    fi
fi

# systemimager source
if [ ! -n "$SISOURCE" ]; then
    SISOURCE=/usr/share/systemimager/boot/$ARCH/standard/initrd_template
fi
if [ ! -d $SISOURCE ]; then
    echo "Directory not found: $SISOURCE"
    echo "Please make sure that the systemimager initrd_template package"
    echo "coresponding to the desired architecture is installed correctly!"
    exit 1
fi

# create temporary copy of template
TDIR=`mktemp -d /tmp/si_dli.XXXXXXX`
CLEANUP="$CLEANUP $TDIR"
rsync -av $SISOURCE/ $TDIR/ >/dev/null

ODIR=`pwd`
# delete unused stuff
cd $TDIR
rm -f linuxrc
rm -f dev.tar.gz
rm -f sbin/init
rm -rf lib/bittorrent
rm -rf lib/udev
rm -rf usr/share
rm -f bin/udp-receiver
rm -f bin/bittorrent-console
rm -f bin/torrentinfo-console
rm -f sbin/init
rm -f sbin/udev*

# write new init
write_init init

cd $ODIR

load_modules

write_output

cleanup
